<template>
  <div class="ivu-ellipsis">
    <slot name="prefix" class="ivu-ellipsis-prefix"></slot>
    <template v-if="computedReady">
      <span class="ivu-ellipsis-text" ref="text">{{ text }}</span>
      <span class="ivu-ellipsis-more" ref="more" v-show="oversize">
        <slot name="more">...</slot>
      </span>
      <slot name="suffix" class="ivu-ellipsis-suffix"></slot>
    </template>
    <div v-else class="ivu-ellipsis-hidden" style="visibility: visible">
      <span class="ivu-ellipsis-text" ref="text">{{ text }}</span>
      <span class="ivu-ellipsis-more" ref="more" v-show="oversize">
        <slot name="more">...</slot>
      </span>
      <slot name="suffix" class="ivu-ellipsis-suffix"></slot>
    </div>
  </div>
</template>
<script>
import camelCase from 'lodash-es/camelCase.js';

const getStyle = (element, styleName) => {
  if (!element || !styleName) return null;
  styleName = camelCase(styleName);
  if (styleName === 'float') {
    styleName = 'cssFloat';
  }
  try {
    const computed = document.defaultView.getComputedStyle(element, '');
    return element.style[styleName] || computed ? computed[styleName] : null;
  } catch (e) {
    return element.style[styleName];
  }
};

const getStrFullLength = (str = '') =>
  str.split('').reduce((pre, cur) => {
    const charCode = cur.charCodeAt(0);
    if (charCode >= 0 && charCode <= 128) {
      return pre + 1;
    }
    return pre + 2;
  }, 0);

const cutStrByFullLength = (str = '', maxLength) => {
  let showLength = 0;
  return str.split('').reduce((pre, cur) => {
    const charCode = cur.charCodeAt(0);
    if (charCode >= 0 && charCode <= 128) {
      showLength += 1;
    } else {
      showLength += 2;
    }
    if (showLength <= maxLength) {
      return pre + cur;
    }
    return pre;
  }, '');
};

export default {
  name: 'Ellipsis',
  emits: ['show', 'hide'],
  props: {
    text: { type: String },
    // 限制高度
    height: { type: Number },
    // 限制行数，将换算为 height。如果设置了 height，则直接使用 height 计算
    lines: { type: Number },
    // 按照指定长度截取
    length: { type: Number },
    // 是否将全角字符的长度视为2来计算字符串长度，适用于 length
    fullWidthRecognition: { type: Boolean, default: false },
    // todo 是否自动根据外层宽度动态改变
    autoResize: { type: Boolean, default: true },
    // 是否禁用
    disabled: { type: Boolean, default: false },
  },
  data() {
    return {
      oversize: false,
      computedReady: false, // 先隐形计算，计算好后，再根据配置显示
      computedText: '', // 计算后的 text 内容
    };
  },
  watch: {
    disabled() {
      this.init();
    },
    text() {
      this.init();
    },
    height() {
      this.init();
    },
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      if (!this.disabled) {
        this.computeText();
        this.limitShow();
      }
    },
    computeText() {
      this.oversize = false;
      this.computedReady = false;

      this.$nextTick(() => {
        let $text = this.$refs.text;
        let $el = this.$el;
        let $more = this.$refs.more;
        let n = 1000;
        let text = this.text;
        let height = this.height;
        // 当 height 未定义，且 lines 定义时，计算真实高度，否则使用 this.height
        if (!height && this.lines) {
          const lineHeight = parseInt(getStyle($el, 'lineHeight'), 10);
          height = lineHeight * this.lines;
        }

        if ($text) {
          // 指定 length，则按具体字数剪裁
          if (this.length) {
            const textLength = this.fullWidthRecognition ? getStrFullLength(text) : text.length;
            if (textLength > this.length) {
              this.oversize = true;
              $more.style.display = 'inline-block';
              text = this.fullWidthRecognition ? cutStrByFullLength(text, this.length) : text.slice(0, this.length);
            }
          } else {
            if ($el.offsetHeight > height) {
              this.oversize = true;
              $more.style.display = 'inline-block';

              while ($el.offsetHeight > height && n > 0) {
                if ($el.offsetHeight > height * 3) {
                  $text.innerText = text = text.substring(0, Math.floor(text.length / 2));
                } else {
                  $text.innerText = text = text.substring(0, text.length - 1);
                }
                n--;
              }
            }
          }
        }

        this.computedText = text;
      });
    },
    limitShow() {
      this.computedReady = true;

      this.$nextTick(() => {
        let $text = this.$refs.text;
        let $el = this.$el;

        if ($text) {
          $text.innerText = this.computedText;
          if ($el.offsetHeight > this.height) {
            this.$emit('hide');
          } else {
            this.$emit('show');
          }
        }
      });
    },
  },
};
</script>

<style lang="less">
.ivu-ellipsis {
  line-height: 1.5;
  .ivu-ellipsis-text {
    word-wrap: break-word;
    word-break: break-all;
    white-space: pre-wrap;
  }
}
</style>
